//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <winhack.h>
_ELASTOS_NAMESPACE_USING

#include <aura.h>
#include <stdio.h>
#include <stdlib.h>

#include "consolewindow.h"
#include "utils.h"

//Handle of subprocess
HANDLE g_hCmdProcess;
//If subprocess was create with CREATE_NEW_WINDOW
Boolean g_bNewWindow = FALSE;

Void CloseCmdProcess()
{
    if (NULL != g_hCmdProcess && !g_bNewWindow) {
            TerminateProcess(g_hCmdProcess, 0);
            CloseHandle(g_hCmdProcess);
            g_hCmdProcess = NULL;
    }

    return ;
}

BOOL __stdcall CtrlCHandleRoutine(DWORD dwCtrlType)
{
    if (CTRL_C_EVENT == dwCtrlType || CTRL_BREAK_EVENT == dwCtrlType) {
        CloseCmdProcess();
    }
    else if (CTRL_CLOSE_EVENT == dwCtrlType || 
             CTRL_SHUTDOWN_EVENT == dwCtrlType) {
        CloseCmdProcess();
        AuraProcess_Exit(0);
    }

    return TRUE;
}

Boolean DoHelp()
{
    PAChar pwszUsage = \
                "Elastos shell command list:\n"
                "\tCLS      Clears the screen.\n"
                "\tCP       Copy a file or a directory.\n"
                "\tCD       Change to a directory.\n"
                "\tDATE     Display the system date.\n"
                "\tDEL      Delete an object.\n"
                "\tDIR      List a directory.\n"
                "\tEXIT     Leave Elastos Shell.\n"
                "\tHELP     Print help message.\n"
                "\tLS       Sort and list a directory.\n"
                "\tMD       Make a directory.\n"
                "\tMKDIR    Make a directory.\n"
                "\tPATH     Displays or sets a search path for executable files.\n"
                "\tRD       Delete a directory.\n"
                "\tREN      Rename a file.\n"
                "\tRENAME   Rename a file.\n"
                "\tRM       Delete an file or a directory.\n"
                "\tRMDIR    Delete an file or a directory.\n"
                "\tSET      Set Stream input and output.\n"
                "\tSTAET    Run Command with a new window.\n"
                "\tTIME     Displays the system time.\n"
                "\tVER      Display Elastos build version.\n";

    Aura_printf(pwszUsage);

    return TRUE;
}

Boolean DoTime()
{
    SYSTEMTIME st;

    GetLocalTime(&st);
    Aura_printf("%02d:%02d:%02d\n", st.wHour, st.wMinute, st.wSecond);

    return TRUE;
}

Boolean DoDate()
{
    SYSTEMTIME st;

    GetLocalTime(&st);
    Aura_printf("Today is %d-%d-%d.\n",st.wYear, st.wMonth, st.wDay);

    return TRUE;
}

Boolean DoPath(PWChar pwszParam)
{
    WChar       path[512];
    Boolean     bRes;

    if (NULL == pwszParam || L'\0' == *pwszParam) {
        Aura_printf("PATH=%ws\n", _wgetenv(L"Path"));
        return TRUE;
    }

    if (L'=' == *pwszParam) {
        pwszParam = ltrim(pwszParam + 1);
    }

    Aura_swprintf((Aura_PWChar)path, (Aura_WChar *)L"Path=%ws", pwszParam);
    bRes = _wputenv(path);

    return bRes;
}

/* Read in a command line */
Void ReadCommand(PWChar const pwszCmdLine, const Int32 maxlen)
{
    WChar c = 0;

    Aura_printf("#");
    for (Int32 i = 0; i < (maxlen - 1); i++)
    {
        c = getwchar();
        if (c == 0xffff) {      //for Control-C
            Aura_printf("\n");
            return ReadCommand(pwszCmdLine, maxlen);
        }
        if (c != L'\n') {
            pwszCmdLine[i] = c;
        }
        else {
            pwszCmdLine[i] = L'\0';
            return;
        }
    }
    pwszCmdLine[maxlen - 1] = L'\0';

    return;
}

/* Execute external command */
Boolean ExecuteCommand(PWChar pwszCmdLine, Int32 nFlag)
{
    STARTUPINFOW            pipe_si;
    PROCESS_INFORMATION     pipe_pi;
    Boolean                 bRes  = FALSE;

    memset((PVoid)&pipe_si, 0, sizeof(STARTUPINFOW));
    pipe_si.cb = sizeof(STARTUPINFOW);
    memset((PVOID)&pipe_pi, 0, sizeof(PROCESS_INFORMATION));
    if (0 == nFlag) {
        pipe_si.dwYCountChars = 5000;
        pipe_si.dwFlags |= STARTF_USECOUNTCHARS;
    }

    bRes = CreateProcessW(NULL, pwszCmdLine, NULL, NULL,
                          TRUE, nFlag, NULL, NULL,
                          &pipe_si, &pipe_pi);
    if (!bRes) {
        return FALSE;
    }
    g_hCmdProcess = pipe_pi.hProcess;
    if (0 == nFlag) {
        WaitForSingleObject(pipe_pi.hProcess, (DWORD)INFINITE);
    }

    return TRUE;
}

/* Notify elservices.exe to exit*/
Void NotifyElServicesExit()
{
    HANDLE hDeregisterPlatServicesEvent = 
                CreateEventW(NULL, FALSE, FALSE, L"DeregisterElServices");
    SetEvent(hDeregisterPlatServicesEvent);
    CloseHandle(hDeregisterPlatServicesEvent);
}

/* Parse cmd and execute */
Void DoCommand(PWChar pwszCmdLine)
{
    WChar         wszCommand[PATH_MAX];
    WChar         wszArgument[PATH_MAX];
    PWChar        pwszCmd    = NULL;
    PWChar        pwszArgs   = NULL;
    PWChar        pwszFind   = NULL;
    UInt32        uCmdLength  = 0;
    UInt32        uArgLength  = 0;
    UInt32        uLength     = 0;
    Boolean       bFileExist  = TRUE;

    uLength = wcslen(pwszCmdLine);

    // find out the blank before the Command ( & 0xFF for tab)
    while ((*pwszCmdLine & 0xFF )== L' ') {
        pwszCmdLine++;
    }

    wszCommand[0] = L'\0';
    pwszFind = wcsncpy(wszCommand, pwszCmdLine, PATH_MAX);
    if (!pwszFind) {
        return ;
    }

    if (wszCommand[0] == L'\0') {
        return ;
    }

    pwszCmd = wszCommand;
    uCmdLength = wcslen(wszCommand);
    pwszArgs = NULL;
    wszArgument[0] = L'\0';
    pwszFind = wszCommand;
    while (*pwszFind != L'\0' && (*pwszFind & 0xFF) != L' ') {
        pwszFind++;
    }

    if (*pwszFind != L'\0') {
        *pwszFind++ = L'\0';
        uCmdLength = wcslen(wszCommand);

        // find out the argument's begin point
        while ((*pwszFind & 0xFF) == L' ') {
            pwszFind++;
        }

        if (*pwszFind != L'\0') {
            wcscpy(wszArgument, pwszFind);
            pwszArgs = wszArgument;

            uArgLength = wcslen(wszArgument);
        }//argument
    }

    InternalCommand enumInterCmd = ConvertInternalSymbol(wszCommand);

    switch (enumInterCmd) {
        case InternalCommand_Cls:
        case InternalCommand_Cd:
        case InternalCommand_Cp:
        case InternalCommand_Del:
        case InternalCommand_Dir:
        case InternalCommand_Ls:
        case InternalCommand_Md:
        case InternalCommand_Rd:
        case InternalCommand_Rename:
        case InternalCommand_Rm:
        case InternalCommand_Set:
            bFileExist = FALSE;
            break;

        case InternalCommand_Path:
            DoPath(wszArgument);
            break;

        case InternalCommand_Exit:
            NotifyElServicesExit();
            AuraProcess_Exit(0);

        case InternalCommand_Help:
            DoHelp();
            break;
 
        case InternalCommand_Time:
            DoTime();
            break;

        case InternalCommand_Date:
            DoDate();
            break;

        case InternalCommand_Ver:
            Aura_printf("Koretide Elastos Console[Version 2.0]\n");
            break;

        case InternalCommand_Start:
            if (NULL == pwszArgs || 
                !ExecuteCommand(pwszArgs, CREATE_NEW_CONSOLE)) {
                _snwprintf(wszCommand, 
                           PATH_MAX, L"%ws %ws\0", wszCommand, pwszArgs);
                bFileExist = FALSE;
            }
            g_bNewWindow = TRUE;
            break;

        case InternalCommand_Not:
            if (NULL != pwszArgs) {
                _snwprintf(wszCommand, 
                           PATH_MAX, L"%ws %ws\0", wszCommand, pwszArgs);
            }
            if (!ExecuteCommand(wszCommand, 0)) {
                bFileExist = FALSE;
            }
            break;

        default:
            bFileExist = FALSE;
            break;
    }

    if (!bFileExist) {
        Aura_printf("'%ws' is not recognized as a command,"
                    "operable program or batch file.\n",
                    wszCommand);
    }
    Aura_printf("\n");

    return ;
}

/* Notiry elservices that console form has been created */
Void NotifyServices()
{
    HANDLE hConsoleEvent = 
                CreateEventW(NULL, FALSE, FALSE, L"StartedElServicesConsole");
    SetEvent(hConsoleEvent);
    CloseHandle(hConsoleEvent);

    return ;
}

int main(int argc, wchar_t * argv[])
{
    WChar wszCommand[PATH_MAX];

    if (!SetConsoleCtrlHandler(CtrlCHandleRoutine, TRUE)) {
        Aura_printf("Init self Ctrl_C handler fail.\n");
    }

    NotifyServices();

    while (TRUE) {
        ReadCommand(wszCommand, PATH_MAX);
        DoCommand(wszCommand);
    }

    return 0;
}
